// headers
#include <core/common/platform.h>
#include <core/common/log.h>
#include <core/common/string.h>
#include <core/common/system.h>
#include <core/common/service.h>
#include <core/common/module.h>
#include <signal.h>

//
// class common::Arguments
//
core::common::Arguments::Arguments()
{
	helpMode_   = false;
	resumeMode_ = false;
	lifeTime_   = 0;
}

core::common::Arguments::Arguments( int argc, char *argv[] )
{
	parse(argc, argv);
}

core::common::Arguments::~Arguments()
{
}

bool core::common::Arguments::parse( int argc, char *argv[] )
{
	assert(argc > 0);

	std::string moduleName = core::common::File::getNameFromPath(argv[0]);
	std::string fileExt    = core::common::File::getExtensionName(moduleName);

	core::common::String::toLower(fileExt);
	if( (fileExt == "exe" || fileExt == "bin") && fileExt.size() < moduleName.size() )
	{
		moduleName = moduleName.substr(0, moduleName.size() - fileExt.size() - 1);
	}

	configFile_ = core::common::String::format("%s/%s.xml", core::common::Module::kConfigName, moduleName.c_str());
	resumeMode_ = false;
	lifeTime_ = 0;

	if( argc > 1 )
	{
		if( strcmp(argv[1],"-h") == 0 || strcmp(argv[1],"/h") == 0 )
		{
			helpMode_ = true;

			// show usage
			fprintf(stdout, "Usage: %s -h or /h for help\n", argv[0]);
			fprintf(stdout, "       %s [arg1] [arg2] [arg3] for normal start, while args described as follows:\n", argv[0]);
			fprintf(stdout, "       [arg1] - config file, relative or absolute path, relative-path is relative to <..> of bin path\n");
			fprintf(stdout, "       [arg2] - '-r' or '/r' to start as resume mode, other for default mode\n");
			fprintf(stdout, "       [arg3] - program life time in seconds, default is 0 as 'forever'\n");
			fprintf(stdout, "       [arg4] - notifier name, for service control\n");
			fflush(stdout);

			return false;
		}
		else if( strcmp(argv[1], "-id") == 0 || strcmp(argv[1], "/id") == 0 )
		{
			helpMode_ = true;

			std::string strIdentify = core::common::SystemProcessor::getIdentify();
			fprintf(stdout, "%s", strIdentify.c_str());

			return false;
		}
		else
		{
			helpMode_ = false;

			if( argc > 1 ) configFile_ = argv[1];
			if( argc > 2 ) resumeMode_ = ((strcmp(argv[2], "-r") == 0) || (strcmp(argv[2], "/r") == 0));
			if( argc > 3 ) lifeTime_ = (time_t)core::common::String::atoi64(argv[3]);
			if( argc > 4 ) notifierName_ = argv[4];
		}
	}
	else
	{
		helpMode_ = false;
	}

	return true;
}

//
// class Common::Service
//

core::common::Service::Service( const core::common::Arguments &args, bool installSignal )
{
	enabled_      = true;
	startTime_    = 0;
	stopTime_     = 0;
	mainThreadId_ = core::common::Thread::getCurrentId();
	args_         = args;

	if( installSignal )
	{
		// install signals
		::signal(SIGHUP,  Service::signalProc);
		::signal(SIGQUIT, Service::signalProc);
		::signal(SIGINT,  Service::signalProc);
		::signal(SIGTERM, Service::signalProc);
#ifdef OS_PLATFORM_WINDOWS
		// suppress the abort message and report on windows
		::_set_abort_behavior(0, _WRITE_ABORT_MSG);
		::_set_abort_behavior(0, _CALL_REPORTFAULT);
#else
		::signal(SIGKILL, Service::signalProc);
#endif

	}
}

core::common::Service::~Service()
{
}

void core::common::Service::signalProc( int code )
{
	switch( code )
	{
	case SIGQUIT:
	case SIGINT:
	case SIGTERM:
	case SIGKILL:
		// terminate
		__ULOG_INFO(__ULOG_FMT("Service", "Get quit signal(%d:%s), stop service..."), code, getSignalName(code));
		getInstance()->onStop();
		break;
	case SIGHUP:
		// terminate
		__ULOG_INFO(__ULOG_FMT("Service", "Get reload signal(%d:%s), reload service..."), code, getSignalName(code));
		getInstance()->onReload();
#ifdef DEBUG_NEW_ENABLE
		DebugNewTraceDetails();
#endif
		break;
	default:
		__ULOG_INFO(__ULOG_FMT("Service", "Get normal signal(%d:%s), ignored"), code, getSignalName(code));
		return;
	}
}

const char *core::common::Service::getSignalName( int code )
{
	const int maxCode = 32;
	static const char *signalNames[maxCode] = 
	{ 
		"SIGNULL",  // 0
		"SIGHUP",   // 1
		"SIGINT",
		"SIGQUIT",
		"SIGILL",
		"SIGTRAP",
		"SIGABRT/SIGIOT",
		"SIGBUS",
		"SIGFPE",
		"SIGKILL",
		"SIGUSR1",
		"SIGSEGV",
		"SIGUSR2",
		"SIGPIPE",
		"SIGALRM",
		"SIGTERM",
		"SIGSTKFLT",
		"SIGCHLD",
		"SIGCONT",
		"SIGSTOP",
		"SIGTSTP",
		"SIGTTIN",
		"SIGTTOU",
		"SIGURG",
		"SIGXCPU",
		"SIGXFSZ",
		"SIGVTALRM",
		"SIGPROF",
		"SIGWINCH",
		"SIGIO/SIGPOLL/SIGLOST",
		"SIGPWR",
		"SIGSYS/SIGUNUSED" // 31
	};

	if( code < 0 || code >= maxCode )
	{
		return "UNKNOWN";
	}

	return signalNames[code];
}

bool core::common::Service::setNotifierName( const std::string &notifierName )
{
	notifier_.close();

	std::string fullNotifierName = core::common::String::format("%s_%s",
		core::common::Module::kNotifierName,
		args_.notifierName_.size() > 0 ? args_.notifierName_.c_str() : notifierName.c_str());

	if( !notifier_.create(fullNotifierName) )
	{
		__ULOG_ERROR(__ULOG_FMT_ERR("Service", "Set notifier name(%s) failed"), fullNotifierName.c_str(), __UERR_CODE, __UERR_STR);
		return false;
	}

	return true;
}
